/**
 * Copyright 2009-2010 - YangJiandong(chunquedong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。

 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 */
package chunmap.model.coord;

/**
 * 精度模型
 * 
 * @author chunquedong
 * 
 */
public class PrecisionModel implements Comparable<PrecisionModel> {
	// 静态预存
	private final static PrecisionModel doubleFloat = new PrecisionModel(
			PrecisionType.DoubleFloat);
	private final static PrecisionModel singleFloat = new PrecisionModel(
			PrecisionType.SingleFloat);
	private final static PrecisionModel fixed = new PrecisionModel(
			PrecisionType.Fixed, 0.01);

	private final double scale;
	private final PrecisionType type;

	// ------------------------------------------------------------构造器与静态方法

	private PrecisionModel(PrecisionType type, double scale) {
		this.type = type;
		this.scale = scale;
	}

	private PrecisionModel(PrecisionType type) {
		this.type = type;
		this.scale = -1;
	}

	public static PrecisionModel getDoubleFloatModel() {
		return doubleFloat;
	}

	public static PrecisionModel getSingleFloatModel() {
		return singleFloat;
	}

	public static PrecisionModel getFixedModel() {
		return fixed;
	}

	public static PrecisionModel getFixedModel(double scale) {
		return new PrecisionModel(PrecisionType.Fixed, scale);
	}

	// 枚举
	public enum PrecisionType {
		Fixed, SingleFloat, DoubleFloat;
	}

	public double getScale() {
		return scale;
	}

	public PrecisionType getType() {
		return type;
	}

	// ------------------------------------------------------------格式化方法

	public double format(double d) {
		if (type == PrecisionType.Fixed) {
			return Math.round(d * scale) / scale;
		} else if (type == PrecisionType.SingleFloat) {
			return (float) d;
		}
		return d;
	}

	public Position format(Position c) {
		if (c instanceof Coordinate2D) {
			return new Coordinate2D(format(c.getX()), format(c.getY()));
		} else if (c instanceof Coordinate3D) {
			return new Coordinate3D(
					format(c.getX()), format(c.getY()),format(c.getZ()));
		} else {
			throw new UnsupportedOperationException();
		}
	}

	@Override
	public int compareTo(PrecisionModel other) {
		if (this.type == other.type) {
			if (this.type != PrecisionType.Fixed) {
				return 0;
			} else {
				return Double.compare(this.scale, other.scale);
			}
		} else {
			return this.type.compareTo(other.type);
		}
	}
	
	public Transform getTransform(){
		return new PrecisionTransform(this);
	}
	
	public static class PrecisionTransform implements Transform{

		private final PrecisionModel percision;
		
		public PrecisionTransform(PrecisionModel percision){
			this.percision=percision;
		}
		
		@Override
		public Position convert(Position p) {
			return percision.format(p);
		}
		
	}
}
